# This is a sample Python script.
import copy

# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import random
import math
import webbrowser


def fun_eval(pop):  # 计算目标值
    obj_value = [[] for i in range(len(pop))]
    for i in range(len(pop)):
        obj1 = rating.score.iloc[pop[i]].mean()
        obj2 = 0
        num = 1
        for a in range(len(pop[i])-1):
            for b in range(a+1,len(pop[i])):
                obj2 = obj2 + (1-item_sim[rating.no.iloc[pop[i][a]]][rating.no.iloc[pop[i][b]]])
                num = num + 1
        obj_value[i].append(obj1)
        obj_value[i].append(obj2/num)
    return obj_value


def domination_sort(cost):
    domination_list = [[] for i in range(len(cost))]
    dominated_no = [0 for i in range(len(cost))]
    front = [[]]
    for i in range(len(cost)):
        for j in range(len(cost)):
            if cost[i][0] >= cost[j][0] and cost[i][1] >= cost[j][1] and not (
                    cost[i][0] == cost[j][0] and cost[i][1] == cost[j][1]):
                domination_list[i].append(j)
                # dominated_no[j] = dominated_no[j] + 1
            elif cost[i][0] <= cost[j][0] and cost[i][1] <= cost[j][1] and not (
                    cost[i][0] == cost[j][0] and cost[i][1] == cost[j][1]):
                dominated_no[i] = dominated_no[i] + 1
                # domination_list[j].append(i)
        if dominated_no[i] == 0:
            front[0].append(i)

    i = 0
    while front[i]:
        temp = []
        for a in front[i]:
            for b in domination_list[a]:
                dominated_no[b] = dominated_no[b] - 1
                if dominated_no[b] == 0:
                    temp.append(b)
        # print(temp)
        front.append(temp)
        i = i + 1
    # print(front)
    return front, dominated_no


def crowd_dis(front, fun_cost, no):
    cost = np.array(fun_cost)
    cost = cost[front]
    dist1 = [0 for i in range(len(front))]
    dist2 = [0 for i in range(len(front))]
    first_index = np.argsort(cost[:, 0])
    second_index = np.argsort(cost[:, 1])
    dist1[first_index[0]] = math.inf
    dist1[first_index[len(front) - 1]] = math.inf
    dist2[second_index[0]] = math.inf
    dist2[second_index[len(front) - 1]] = math.inf
    for i in range(1, len(front) - 1):
        dist1[first_index[i]] = abs(cost[first_index[i - 1], 0] - cost[first_index[i + 1], 0])
        dist2[second_index[i]] = abs(cost[second_index[i - 1], 1] - cost[second_index[i - 1], 1])
    dist = np.add(dist2, dist1)
    front_index = np.argsort(dist)
    new_front = []
    lens = len(front) - 1
    # print(str(len(front))+' '+str(lens)+' '+str(len(front_index)))
    for i in range(no):
        new_front.append(front[front_index[lens - i]])
    return new_front


def mutation(recommend_list):  #
    for i in range(List_num):
        if random.random() < 0.3:
            recommend_list[i] = random.randint(lb, rb)
    return recommend_list


def find_index(ind_, left, right, val):
    index = math.inf
    # print(len(ind_), " ", left, " ", right)
    for i in range(left):
        if ind_[i] == val:
            index = i
    if right != List_num - 1:
        for i in range(right + 1, List_num):
            if ind_[i] == val:
                index = i - List_num
    # print(len(ind_), " ", left, " ", right)
    return index


def crossover_and_mutation(old_pop):  #交叉变异
    new_pop_ = []
    for i in range(round(Pop_size / 2)):
        index1 = random.randint(0, Pop_size - 1)
        index2 = random.randint(0, Pop_size - 1)
        while index1 == index2:
            index2 = random.randint(0, Pop_size - 1)
        father1 = copy.deepcopy(old_pop[index1])
        father2 = copy.deepcopy(old_pop[index2])
        left = random.randint(0, List_num - 1)
        right = random.randint(0, List_num - 1)
        if left > right:
            temp = left
            left = right
            right = temp
        up = copy.deepcopy(father1[left:right + 1])
        down = copy.deepcopy(father2[left:right + 1])
        father1[left:right + 1] = copy.deepcopy(down)
        father2[left:right + 1] = copy.deepcopy(up)
        for seat in range(len(up)):
            index = find_index(father2, left, right, up[seat])
            if index != math.inf:
                father2[index] = down[seat]
        for seat in range(len(down)):
            index = find_index(father1, left, right, down[seat])
            if index != math.inf:
                father1[index] = up[seat]
        # print(len(father1),len(father2))
        if random.random() < mutation_p:
            # new_son = mutation(old_pop[random.randint(0, Pop_size - 1)])
            # new_pop_.append(new_son)
            #mut_index = random.randint(0, Pop_size - 1)
            #father = copy.deepcopy(old_pop[mut_index])
            #son = mutation(father)
            #new_pop_.append(son)
            father1 = mutation(father1)
            father2 = mutation(father2)
        new_pop_.append(father1)
        new_pop_.append(father2)
    return new_pop_


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    rating = pd.read_csv('recommend.csv',names=['no','score'])
    item_sim = pd.read_csv('item_sim.csv',header=None)
    lb = 0
    rb = 284
    # rb = 9064  # 上下界
    List_num = 10
    Pop_size = 100
    Max_gen = 300
    mutation_p = 1
    Pop = [random.sample(range(lb, rb), List_num) for i in range(Pop_size)]
    cost = fun_eval(Pop)
    sorted_index, dominated_no = domination_sort(cost)
    plt.ion()  # 开启交互模式
    plt.subplots()
    # sorted_index = domination_sort(cost)
    gen_no = 1
    while gen_no < Max_gen:
        #new_pop = mult_crossover_and_mutation(Pop,dominated_no)
        new_pop = crossover_and_mutation(Pop)
        new_cost = fun_eval(new_pop)
        cost = cost + new_cost
        Pop = Pop + new_pop
        sorted_index, dominated_no = domination_sort(cost)
        # print(sorted_index)
        surplus = Pop_size
        front_no = 0
        sub_pop = []
        sub_cost = []
        while surplus >= len(sorted_index[front_no]):
            for i in range(len(sorted_index[front_no])):
                # print(len(Pop[sorted_index[front_no][i]]))
                sub_pop.append(Pop[sorted_index[front_no][i]])
                sub_cost.append(cost[sorted_index[front_no][i]])
            surplus = surplus - len(sorted_index[front_no])
            front_no = front_no + 1
        front_id = crowd_dis(sorted_index[front_no], cost, surplus)
        for i in range(surplus):
            sub_pop.append(Pop[front_id[i]])
            sub_cost.append(cost[front_id[i]])
            # sub_pop.append(Pop[sorted_index[front_no][i]])
            # sub_cost.append(cost[sorted_index[front_no][i]])
            # print(len(Pop[sorted_index[front_no][i]]))
        Pop = copy.deepcopy(sub_pop)
        cost = copy.deepcopy(sub_cost)
        function = np.array(cost)
        function = np.transpose(function, [1, 0])
        plt.clf()  # 清空画布
        plt.title(gen_no)
        plt.xlabel('ratting', fontsize=15)
        plt.ylabel('diversity', fontsize=15)
        plt.scatter(function[0], function[1],s=10, c="grey")
        plt.pause(0.2)
        gen_no = gen_no + 1
    sorted_index, dominated_no = domination_sort(cost)
    front_ = sorted_index[0]  # front_存储前沿个体在pop中的位置
    function = np.array(cost)
    #function = np.transpose(function, [1, 0])
    # for p in range(Pop_size):
    #    function1.append(cost[p][0])
    #    function2.append(cost[p][1])
    pareto = function[front_]
    plt_pareto = np.transpose(pareto, [1, 0])
    plt.scatter(plt_pareto[0], plt_pareto[1],s=10,color = 'r')
    pareto_index = np.argsort(pareto[:, 0])
    Ind_index = []
    for aa in range(0, round(len(front_))):
        index_ = front_[pareto_index[aa]]
        Ind_index = Ind_index + Pop[index_]
    recommend_index = list(set(Ind_index))
    #recommened_list = Doctor.iloc[recommend_index].index
    #print(recommened_list)
    plt.ioff()
    plt.show()
# See PyCharm help at https://www.jetbrains.com/help/pycharm/
